﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BomberManServer.Gaming.Serialization;
using System.Net.Sockets;
using System.IO;
using BomberManServer.Gaming.Object;
using BomberManServer.Gaming.Serialization.Object.Player;
using BomberManServer.Gaming.Serialization.Object.Map;
using BomberManServer.Gaming.Serialization.Object.Connection;
using BomberManServer.Gaming.Object.Mapping.Items;
using BomberManServer.Gaming.Serialization.Object.Bomb;
using BomberManServer.Gaming.Serialization.Object.Block;
using BomberManServer.Utils;

namespace BomberManServer.Gaming.Socket
{
    public class ActionSender : InGame
    {
        // the players
        List<PlayerSocket> players;
        // internal serializer
        Serializer serializer;
        // the messages that will be broadcast
        List<MemoryStream> messages;

        private MemoryStream bufferMapInitialize;

        public ActionSender(Game game) : base(game)
        {
            this.players = game.connections.connected;
            serializer = game.serializer;
            messages = new List<MemoryStream>();
            bufferizeMapInitialize();
        }

        // TODO we need to send it NOW
        // send a message to one man
        private void send(PlayerSocket player,byte[] message) {
            player.client.Client.Send(message,SocketFlags.None);
        }

        // send all the messages
        public void compute()
        {
            // send all messages
            foreach (MemoryStream message in messages)
                foreach (PlayerSocket player in players)
                {
                    try
                    {
                        message.WriteTo(player.client.GetStream());
                    }
                    catch (IOException ex)
                    {
                        // TODO
                        // disconnect the player
                    }
                    catch (InvalidOperationException ex)
                    {
                        // TODO
                        // disconnect the player
                    }
                }
            // empty all messages
            messages.Clear();
        }

        /// INPUT METHODS
        /// Those functions are called to queue the messages
        /// The messages will be sent later
        /// Those functions are generating the binary message
        /// Function name shall start by "onX"

        public void onMapReset(uint seed)
        {
            // create the message
            MemoryStream stream = new MemoryStream();
            // add the header
            stream.WriteByte((byte)Message.MapReset);
            // add the parameters
            MapReset map;
            map.seed = seed;
            serializer.Serialize(stream,map);
            // queue the message
            messages.Add(stream);
        }

        public void onPlayerMove(Player player) {
            // create the message
            MemoryStream stream = new MemoryStream();
            // add the header
            stream.WriteByte((byte)Message.PlayerMove);
            PlayerMove target = new PlayerMove();
            target.player_id = (UInt16) player.id;
            target.x = (UInt16) player.avatar.coord.xPoint;
            target.y = (UInt16) player.avatar.coord.yPoint;
            serializer.Serialize(stream, target);
            // queue the message
            messages.Add(stream);            
        }

        public void onMapInitialize(PlayerSocket player)
        {
            bufferMapInitialize.WriteTo(player.client.GetStream());
        }

        // the message you send to the client when he just sent token
        public void onIdentified(PlayerSocket player)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.Identified);
            Identified target = new Identified();
            target.id = (ushort) player.player.id;
            serializer.Serialize(buffer, target);

            buffer.WriteTo(player.client.GetStream());
        }

        // broadcast that player is now connected
        public void onConnected(Player player)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.PlayerConnected);
            Identified target = new Identified();
            target.id = (ushort) player.id;
            serializer.Serialize(buffer, target);

            messages.Add(buffer);
        }

        // broadcast that player is now ready
        public void onReady(Player player)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.PlayerReady);
            Identified target = new Identified();
            target.id = (ushort) player.id;
            serializer.Serialize(buffer, target);

            messages.Add(buffer);
        }

        public void onBombSet(Bomb bomb)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.BombSet);

            BombSet target = new BombSet();
            target.player = (UInt16)bomb.owner.id;
            target.x = (UInt16)bomb.coord.xBlock;
            target.y = (UInt16)bomb.coord.yBlock;

            serializer.Serialize(buffer, target);

            messages.Add(buffer);
        }

        public void onBombExplode(Bomb bomb)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.BombExplode);

            BombExplode target = new BombExplode();
            target.player = (UInt16)bomb.owner.id;
            target.length = (UInt16)bomb.deflag_length;

            serializer.Serialize(buffer, target);

            messages.Add(buffer);
        }

        // create the buffer for the MapInitialize message
        private void bufferizeMapInitialize() {
            bufferMapInitialize = new MemoryStream();
            // add the header
            bufferMapInitialize.WriteByte((byte)Message.Initialize);
            bufferMapInitialize.WriteByte((byte)game.match.type);
            switch (game.match.type)
            {
                case BomberManServer.Gaming.Object.Match.MatchType.FFA:
                case BomberManServer.Gaming.Object.Match.MatchType.TEAM:
                    serializer.Serialize(bufferMapInitialize, game.match.players[0].name);
                    serializer.Serialize(bufferMapInitialize, game.match.players[1].name);
                    serializer.Serialize(bufferMapInitialize, game.match.players[2].name);
                    serializer.Serialize(bufferMapInitialize, game.match.players[3].name);
                    break;
                case BomberManServer.Gaming.Object.Match.MatchType.SOLO:
                    serializer.Serialize(bufferMapInitialize, game.match.players[0].name);
                    serializer.Serialize(bufferMapInitialize, game.match.players[1].name);
                    break;
            }
        }
        public void onBlockDestroyed(Coordinates coord)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.BlockDestroyed);

            BlockDestroyed target = new BlockDestroyed();
            target.x = (UInt16)coord.xBlock;
            target.y = (UInt16)coord.yBlock;
            serializer.Serialize(buffer, target);

            messages.Add(buffer);
        }
        
        // for bonus temp
        public void onBonusSpawn(Coordinates coord)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.BonusSpawn);

            BonusSpawn target = new BonusSpawn();
            target.x = (UInt16)coord.xBlock;
            target.y = (UInt16)coord.yBlock;
            serializer.Serialize(buffer, target);

            messages.Add(buffer);
        }

        // for bonus permanent
        public void onBonusDrop(Coordinates coord)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.BonusDrop);

            BlockDestroyed target = new BlockDestroyed();
            target.x = (UInt16)coord.xBlock;
            target.y = (UInt16)coord.yBlock;
            serializer.Serialize(buffer, target);

            messages.Add(buffer);
        }

        public void onBonusTaken(Coordinates coord)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.BonusTaken);

            BonusTaken target = new BonusTaken();
            target.x = (UInt16)coord.xBlock;
            target.y = (UInt16)coord.yBlock;

            serializer.Serialize(buffer, target);

            messages.Add(buffer);
        }

        public void onPlayerKilled(Player player)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.PlayerKilled);

            PlayerKilled target = new PlayerKilled();
            target.playerID = (UInt16)player.id;

            serializer.Serialize(buffer, target);

            messages.Add(buffer);
        }

        public void onScoreBoard(Match match)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.ScoreBoard);
            //buffer.WriteByte((byte)game.match.type);
            foreach (Team team in match.teams)
            {
                ScoreBoardTeam target = new ScoreBoardTeam();
                target.score = game.stats.getScore(team.id);

                serializer.Serialize(buffer, target);
            }
            foreach (Player player in match.players)
            {
                ScoreBoardPlayer target = new ScoreBoardPlayer();

                target.kills = game.stats.getPlayer(player.uid).nbKill;
                target.deaths = game.stats.getPlayer(player.uid).nbDeath;

                serializer.Serialize(buffer, target);
            }
            messages.Add(buffer);
        }

        public void onEndGame(Match match)
        {
            MemoryStream buffer = new MemoryStream();
            // add the header
            buffer.WriteByte((byte)Message.ScoreBoard);
            //buffer.WriteByte((byte)game.match.type);

            ScoreBoardWinner winner = new ScoreBoardWinner();
            winner.winnerID = (UInt16)game.stats.getWinnerID();

            serializer.Serialize(buffer, winner);

            foreach (Team team in match.teams)
            {
                ScoreBoardTeam target = new ScoreBoardTeam();
                target.score = game.stats.getScore(team.id);

                serializer.Serialize(buffer, target);
            }
            foreach (Player player in match.players)
            {
                ScoreBoardPlayer target = new ScoreBoardPlayer();

                target.kills = game.stats.getPlayer(player.id).nbKill;
                target.deaths = game.stats.getPlayer(player.id).nbDeath;

                serializer.Serialize(buffer, target);
            }
            messages.Add(buffer);
        }

        public void onBombKick(Direction direction, Coordinates coord)
        {
            // create the message
            MemoryStream stream = new MemoryStream();
            // add the header
            stream.WriteByte((byte)Message.BombKicked);
            BombKicked target = new BombKicked();
            target.x = (UInt16)coord.xBlock;
            target.y = (UInt16)coord.yBlock;
            target.direction = (UInt16)direction;
            serializer.Serialize(stream, target);
            // queue the message
            messages.Add(stream);

        }
        public void onSlabOff(Coordinates coord)
        {
            // create the message
            MemoryStream stream = new MemoryStream();
            // add the header
            stream.WriteByte((byte)Message.SlabOff);
            SlabOff target = new SlabOff();
            target.x = (UInt16)coord.xBlock;
            target.y = (UInt16)coord.yBlock;
            serializer.Serialize(stream, target);
            // queue the message
            messages.Add(stream);

        }
        public void onSlabOn(Coordinates coord)
        {
            // create the message
            MemoryStream stream = new MemoryStream();
            // add the header
            stream.WriteByte((byte)Message.SlabOn);
            SlabOff target = new SlabOff();
            target.x = (UInt16)coord.xBlock;
            target.y = (UInt16)coord.yBlock;
            serializer.Serialize(stream, target);
            // queue the message
            messages.Add(stream);

        }
    }
}
